﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MatrixContainer.cs" company="">
//   
// </copyright>
// <summary>
//  An image that represents a grid of stuff and manage the navigation through them.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

/*
 * Author: Juan Mompean Esteban
 * Created: 1-12-2011
 */
namespace ITU_vs_Zombies.Graphics
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;

    using ITU_vs_Zombies.Menu;

    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;

    /// <summary>
    /// An image that represents a grid of stuff and manage the navigation through them.
    /// </summary>
    public class MatrixContainer
    {
        #region Constants and Fields

        /// <summary>
        /// The cols.
        /// </summary>
        private readonly int cols;

        /// <summary>
        /// A matrix for the name of the position and the name of the image in that position.
        /// </summary>
        private readonly Tuple<string, Texture2D>[,] matrix;

        /// <summary>
        /// The rows.
        /// </summary>
        private readonly int rows;

        /// <summary>
        /// The size.
        /// </summary>
        private readonly int size;

        /// <summary>
        /// The arial.
        /// </summary>
        private SpriteFont arial;

        /// <summary>
        /// The focus.
        /// </summary>
        private Vector2 focus;

        /// <summary>
        /// The focus texture.
        /// </summary>
        private Texture2D focusTexture;

        /// <summary>
        /// The page number.
        /// </summary>
        private int pageNumber;

        /// <summary>
        /// The page selected.
        /// </summary>
        private int pageSelected;

        /// <summary>
        /// The selected.
        /// </summary>
        private Vector2 selected;

        /// <summary>
        /// The selected texture.
        /// </summary>
        private Texture2D selectedTexture;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MatrixContainer"/> class. 
        /// Initializes a new MatrixContainer.
        /// </summary>
        /// <param name="list">
        /// The list.
        /// </param>
        /// <param name="rows">
        /// The rows.
        /// </param>
        /// <param name="cols">
        /// The cols.
        /// </param>
        public MatrixContainer(List<Tuple<string, Texture2D>> list, int rows, int cols)
        {
            Contract.Requires(list != null);
            Contract.Requires(cols > 0);
            Contract.Requires(rows > 0);
            int size = (list.Count / cols) + (list.Count % cols != 0 ? 1 : 0);
            this.matrix = new Tuple<string, Texture2D>[size, cols];
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < cols && list.Count > (i * cols) + j; j++)
                {
                    this.matrix[i, j] = list[(i * cols) + j];
                }
            }

            this.cols = cols;
            this.rows = rows;
            this.pageNumber = 0;
            this.focus = new Vector2(0, 0);
            this.size = list.Count;
            this.selected = new Vector2(-1, -1);
            this.pageSelected = int.MinValue;
        }

        #endregion

        #region Public Events

        /// <summary>
        /// The enter pressed.
        /// </summary>
        public event DelegateKeyPressed.KeyPressed EnterPressed;

        #endregion

        #region Public Methods

        /// <summary>
        /// The dispose.
        /// </summary>
        public void Dispose()
        {
            this.focusTexture.Dispose();
            foreach (var tuple in this.matrix)
            {
                if (tuple != null)
                {
                    tuple.Item2.Dispose();
                }
            }
        }

        /// <summary>
        /// The draw.
        /// </summary>
        /// <param name="screenWidth">
        /// The screen width.
        /// </param>
        /// <param name="screenHeight">
        /// The screen height.
        /// </param>
        /// <param name="spriteBatch">
        /// The sprite batch.
        /// </param>
        public void Draw(int screenWidth, int screenHeight, SpriteBatch spriteBatch)
        {
            Contract.Requires(spriteBatch != null);
            Contract.Requires(screenWidth > 0);
            Contract.Requires(screenHeight > 0);
            int stuffWidth = screenWidth / (this.cols + 2);
            int stuffHeight = screenHeight / (this.rows + 2);
            int gapWidth = (screenWidth - (stuffWidth * this.cols)) / (this.cols + 1);
            int gapHeight = (screenHeight - (stuffHeight * this.rows)) / (this.rows + 1);
            var currentPosition = new Vector2(0, 0);

            spriteBatch.Draw(
                this.focusTexture, 
                new Rectangle(
                    (int)(gapWidth + (this.focus.X * (gapWidth + stuffWidth))), 
                    (int)(gapHeight + (this.focus.Y * (stuffHeight + gapHeight))), 
                    stuffWidth, 
                    stuffHeight), 
                Color.White);
            if (this.IsAValidPosition(this.selected) && this.pageSelected == this.pageNumber)
            {
                spriteBatch.Draw(
                    this.selectedTexture, 
                    new Rectangle(
                        (int)(gapWidth + (this.selected.X * (gapWidth + stuffWidth))), 
                        (int)(gapHeight + (this.selected.Y * (stuffHeight + gapHeight))), 
                        stuffWidth, 
                        stuffHeight), 
                    Color.White);
            }

            for (int k = this.pageNumber * this.rows;
                 k < (this.pageNumber + 1) * this.rows && (k * this.cols) < this.size;
                 k++)
            {
                for (int l = 0; l < this.matrix.GetLength(1) && this.matrix[k, l] != null; l++)
                {
                    spriteBatch.Draw(
                        this.matrix[k, l].Item2, 
                        new Rectangle(
                            (int)(gapWidth + (currentPosition.X * (gapWidth + stuffWidth))), 
                            (int)(gapHeight + (currentPosition.Y * (stuffHeight + gapHeight))), 
                            stuffWidth, 
                            stuffHeight), 
                        Color.White);

                    spriteBatch.DrawString(
                        this.arial, 
                        this.matrix[k, l].Item1, 
                        new Vector2(
                            (int)(gapWidth + (currentPosition.X * (gapWidth + stuffWidth))), 
                            (int)(gapHeight + (currentPosition.Y * (stuffHeight + gapHeight)) + stuffHeight)), 
                        Color.GreenYellow, 
                        0, 
                        Vector2.Zero, 
                        1, 
                        SpriteEffects.None, 
                        1);
                    currentPosition.X++;
                }

                currentPosition.X = 0;
                currentPosition.Y++;
            }
        }

        /// <summary>
        /// The load content.
        /// </summary>
        /// <param name="content">
        /// The content.
        /// </param>
        public void LoadContent(ContentManager content)
        {
            Contract.Requires(content != null);
            this.focusTexture = content.Load<Texture2D>(@"Profiles\backProfile");
            this.selectedTexture = content.Load<Texture2D>(@"Profiles\selectedProfile");
            this.arial = content.Load<SpriteFont>(@"fonts\Arial");
        }

        /// <summary>
        /// The move.
        /// </summary>
        /// <param name="inputState">
        /// The input State.
        /// </param>
        public void Move(InputState inputState)
        {
            var up = new InputAction(new[] { Keys.Up }, true);
            var down = new InputAction(new[] { Keys.Down }, true);
            var right = new InputAction(new[] { Keys.Right }, true);
            var left = new InputAction(new[] { Keys.Left }, true);
            var enter = new InputAction(new[] { Keys.Enter }, true);

            if (up.Evaluate(inputState) && this.focus.Y > 0)
            {
                this.focus.Y--;
            }
            else if (up.Evaluate(inputState) && (this.focus.Y % this.rows) == 0 && this.pageNumber > 0)
            {
                this.pageNumber--;
                this.focus.X = 0;
                this.focus.Y = 0;
            }

            if (down.Evaluate(inputState) && this.IsAValidPosition(new Vector2(this.focus.X, this.focus.Y + 1)))
            {
                this.focus.Y++;
            }
            else if (down.Evaluate(inputState) && this.focus.Y == (this.rows - 1)
                     && this.pageNumber < (this.matrix.Length - 1) / (this.cols * this.rows))
            {
                this.pageNumber++;
                this.focus.X = 0;
                this.focus.Y = 0;
            }

            if (right.Evaluate(inputState) && this.IsAValidPosition(new Vector2(this.focus.X + 1, this.focus.Y)))
            {
                this.focus.X++;
            }

            if (left.Evaluate(inputState) && this.focus.X > 0)
            {
                this.focus.X--;
            }

            if (enter.Evaluate(inputState) && this.EnterPressed != null)
            {
                this.selected = this.focus;
                this.pageSelected = this.pageNumber;

                // The matrix represented in the screen use the coordinates in reverse order.
                this.EnterPressed(
                    this.matrix[(int)((this.pageNumber * this.rows) + this.selected.Y), (int)this.selected.X].Item1);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// The is a valid position.
        /// </summary>
        /// <param name="position">
        /// The position.
        /// </param>
        /// <returns>
        /// The is a valid position.
        /// </returns>
        private bool IsAValidPosition(Vector2 position)
        {
            return position.X >= 0 && position.Y >= 0 && position.X < this.cols && position.Y < this.rows
                   &&
                   ((this.pageNumber * this.cols * this.rows) + (position.Y * this.cols) + position.X + 1) <= this.size;
        }

        #endregion
    }
}